//
// Created by Administrator on 2022/10/22 0022.
//
#include <onix/types.h>
#include <onix/onix.h>
#include <onix/interrupt.h>
#include <onix/debug.h>
#include <onix/io.h>
#include <onix/task.h>
#include <onix/assert.h>
#include <onix/thread.h>

#define PIT_CHAN0_REG   0x40
#define PIT_CHAN2_REG   0x42
/*   0   1   2   3   4   5   6   7
 * +---+---+---+---+---+---+---+---+
 * |BCD| M0| M1| M2|RL0|RL1|SC0|SC1|
 * +---+---+---+---+---+---+---+---+
 * SC: (Select Counter)计数器选择位
 *      00: counter0
 *      01: counter1
 *      10: counter2
 *      11: 无效
 * RL: (Read Load)读写操作位
 *      00: 锁存数据
 *      01: 只读写低字节
 *      10: 只读写高字节
 *      11: 先读写低字节，后读写高字节
 * M: (Mode)模式选择
 *      000: 模式0
 *      001: 模式1
 *      x10: 模式2
 *      x11: 模式3
 *      100: 模式4
 *      101: 模式5
 * BCD: (Binary Coded Decimal)码
 *      0: 表示二进制计数器
 *      1: 二进制编码的十进制计数器
 * */
#define PiT_CTRL_REG    0x43

#define HZ              100
#define OSCILLATOR      1193182
#define CLOCK_COUNTER   (OSCILLATOR / HZ)
#define JIFFY           (1000 / HZ)

#define SPEAKER_REG     0x61
#define BEEP_HZ         440
#define BEEP_COUNTER    (OSCILLATOR / BEEP_HZ)

u32 volatile jiffies = 0;
u32 volatile beeping = 0;
u32 jiffy = JIFFY;

void start_beep()
{
    DEBUGK("Beeping...\n");
    if (!beeping) {
        outb(SPEAKER_REG, inb(SPEAKER_REG) | 0b11);
    }
    beeping = jiffies + 5;
}

void stop_beeping()
{
    if (beeping && jiffies > beeping) {
        outb(SPEAKER_REG, inb(SPEAKER_REG) & 0xFC);
        beeping = 0;
    }
}

void clock_handler(int vector)
{
    send_eoi(vector);
    stop_beeping();
    task_wakeup();

    jiffies ++;

    task_t *task = running_task();
    assert(task->magic == ONIX_MAGIC);
    task->jiffies = jiffies;
    task->ticks--;
    if (!task->ticks) {
        task->ticks = task->priority;
        schedule();
    }
}

time_t sys_time()
{
    return get_startup_time() + (jiffies + JIFFY) / 1000;
}

void pit_init()
{
    // 二进制计数器 模式2 先读写低字节，后读写高字节 counter0
    outb(PiT_CTRL_REG, 0b00110100);
    outb(PIT_CHAN0_REG, CLOCK_COUNTER & 0xFF);
    outb(PIT_CHAN0_REG, (CLOCK_COUNTER >> 8) & 0xFF);

    // 配置蜂鸣器
    outb(PiT_CTRL_REG, 0b10110110);
    outb(PIT_CHAN2_REG, (u8)BEEP_COUNTER);
    outb(PIT_CHAN2_REG, (u8)BEEP_COUNTER >> 8);
}

void clock_init()
{
    pit_init();
    set_interrupt_handler(IRQ_CLOCK, clock_handler);
    set_interrupt_mask(IRQ_CLOCK, TRUE);
}